/*
    Copyright (C) 2007 by Robert Knight <robertknight@gmail.com>

    Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301  USA.
*/

// Own
#include "ScreenWindow.h"

// Qt
#include <QtCore>

// Konsole
#include "Screen.h"

using namespace Konsole;

ScreenWindow::ScreenWindow(QObject * parent)
  : QObject(parent)
  , _windowBuffer(0)
  , _windowBufferSize(0)
  , _bufferNeedsUpdate(true)
  , _windowLines(1)
  , _currentLine(0)
  , _trackOutput(true)
  , _scrollCount(0) {
}
ScreenWindow::~ScreenWindow() {
  delete[] _windowBuffer;
}
void ScreenWindow::setScreen(Screen * screen) {
  Q_ASSERT( screen );

  _screen = screen;
}

Screen * ScreenWindow::screen() const {
  return _screen;
}

Character * ScreenWindow::getImage() {
  // reallocate internal buffer if the window size has changed
  int size = windowLines() * windowColumns();
  if (_windowBuffer == 0 || _windowBufferSize != size) {
    delete[] _windowBuffer;
    _windowBufferSize = size;
    _windowBuffer = new Character[size];
    _bufferNeedsUpdate = true;
  }

  if (!_bufferNeedsUpdate) {
    return _windowBuffer;
  }

  _screen->getImage(_windowBuffer,size,
                    currentLine(),endWindowLine());

  // this window may look beyond the end of the screen, in which
  // case there will be an unused area which needs to be filled
  // with blank characters
  fillUnusedArea();

  _bufferNeedsUpdate = false;
  return _windowBuffer;
}

void ScreenWindow::fillUnusedArea() {
  int screenEndLine = _screen->getHistLines() + _screen->getLines() - 1;
  int windowEndLine = currentLine() + windowLines() - 1;

  int unusedLines = windowEndLine - screenEndLine;
  int charsToFill = unusedLines * windowColumns();

  Screen::fillWithDefaultChar(_windowBuffer + _windowBufferSize - charsToFill,charsToFill);
}

// return the index of the line at the end of this window, or if this window
// goes beyond the end of the screen, the index of the line at the end
// of the screen.
//
// when passing a line number to a Screen method, the line number should
// never be more than endWindowLine()
//
int ScreenWindow::endWindowLine() const {
  return qMin(currentLine() + windowLines() - 1,
              lineCount() - 1);
}
QVector<LineProperty> ScreenWindow::getLineProperties() {
  QVector<LineProperty> result = _screen->getLineProperties(currentLine(),endWindowLine());

  if (result.count() != windowLines()) {
    result.resize(windowLines());
  }

  return result;
}

QString ScreenWindow::selectedText( bool preserveLineBreaks ) const {
  return _screen->selectedText( preserveLineBreaks );
}

void ScreenWindow::getSelectionStart( int & column , int & line ) {
  _screen->getSelectionStart(column,line);
  line -= currentLine();
}
void ScreenWindow::getSelectionEnd( int & column , int & line ) {
  _screen->getSelectionEnd(column,line);
  line -= currentLine();
}
void ScreenWindow::setSelectionStart( int column , int line , bool columnMode ) {
  _screen->setSelectionStart( column , qMin(line + currentLine(),endWindowLine())  , columnMode);

  _bufferNeedsUpdate = true;
  emit selectionChanged();
}

void ScreenWindow::setSelectionEnd( int column , int line ) {
  _screen->setSelectionEnd( column , qMin(line + currentLine(),endWindowLine()) );

  _bufferNeedsUpdate = true;
  emit selectionChanged();
}

bool ScreenWindow::isSelected( int column , int line ) {
  return _screen->isSelected( column , qMin(line + currentLine(),endWindowLine()) );
}

void ScreenWindow::clearSelection() {
  _screen->clearSelection();

  emit selectionChanged();
}

void ScreenWindow::setWindowLines(int lines) {
  Q_ASSERT(lines > 0);
  _windowLines = lines;
}
int ScreenWindow::windowLines() const {
  return _windowLines;
}

int ScreenWindow::windowColumns() const {
  return _screen->getColumns();
}

int ScreenWindow::lineCount() const {
  return _screen->getHistLines() + _screen->getLines();
}

int ScreenWindow::columnCount() const {
  return _screen->getColumns();
}

QPoint ScreenWindow::cursorPosition() const {
  QPoint position;

  position.setX( _screen->getCursorX() );
  position.setY( _screen->getCursorY() );

  return position;
}

int ScreenWindow::currentLine() const {
  return qBound(0,_currentLine,lineCount()-windowLines());
}

void ScreenWindow::scrollBy( RelativeScrollMode mode , int amount ) {
  if ( mode == ScrollLines ) {
    scrollTo( currentLine() + amount );
  } else if ( mode == ScrollPages ) {
    scrollTo( currentLine() + amount * ( windowLines() / 2 ) );
  }
}

bool ScreenWindow::atEndOfOutput() const {
  return currentLine() == (lineCount()-windowLines());
}

void ScreenWindow::scrollTo( int line ) {
  int maxCurrentLineNumber = lineCount() - windowLines();
  line = qBound(0,line,maxCurrentLineNumber);

  const int delta = line - _currentLine;
  _currentLine = line;

  // keep track of number of lines scrolled by,
  // this can be reset by calling resetScrollCount()
  _scrollCount += delta;

  _bufferNeedsUpdate = true;

  emit scrolled(_currentLine);
}

void ScreenWindow::setTrackOutput(bool trackOutput) {
  _trackOutput = trackOutput;
}

bool ScreenWindow::trackOutput() const {
  return _trackOutput;
}

int ScreenWindow::scrollCount() const {
  return _scrollCount;
}

void ScreenWindow::resetScrollCount() {
  _scrollCount = 0;
}

QRect ScreenWindow::scrollRegion() const {
  bool equalToScreenSize = windowLines() == _screen->getLines();

  if ( atEndOfOutput() && equalToScreenSize ) {
    return _screen->lastScrolledRegion();
  } else {
    return QRect(0,0,windowColumns(),windowLines());
  }
}

void ScreenWindow::notifyOutputChanged() {
  // move window to the bottom of the screen and update scroll count
  // if this window is currently tracking the bottom of the screen
  if ( _trackOutput ) {
    _scrollCount -= _screen->scrolledLines();
    _currentLine = qMax(0,_screen->getHistLines() - (windowLines()-_screen->getLines()));
  } else {
    // if the history is not unlimited then it may
    // have run out of space and dropped the oldest
    // lines of output - in this case the screen
    // window's current line number will need to
    // be adjusted - otherwise the output will scroll
    _currentLine = qMax(0,_currentLine -
                        _screen->droppedLines());

    // ensure that the screen window's current position does
    // not go beyond the bottom of the screen
    _currentLine = qMin( _currentLine , _screen->getHistLines() );
  }

  _bufferNeedsUpdate = true;

  emit outputChanged();
}

//#include "moc_ScreenWindow.cpp"
